我对学习delphiTeacher的《delphi调用及封装Android原生控件》的学习和研究点滴体会之二 | 您所在的位置:网站首页 › Delphi xe10 ui控件 › 我对学习delphiTeacher的《delphi调用及封装Android原生控件》的学习和研究点滴体会之二 |
我对学习delphiTeacher的《delphi调用及封装Android原生控件》的
学习和研究点滴体会之二
摘要: 本文为3月21日晚学习了delphiTeacher王老师的《delphi调用及封装Android原生控件》后的体会之二。 关于Android原生应用程序、Android原生窗体、Android原生窗体中的控件和delphi FMX之间的对应关系 1234567一、关于Android原生应用程序、Android原生窗体、Android原生窗体中的控件和delphi FMX之间的对应关系 1.1、单元引用关系 {$IFDEF ANDROID} FMX.Platform.Android, //:delphi FMX平台和Android安卓操作系统的对接的接口单元 //:其中引用了FMX原生调用Android原生对象的入口单元 :Androidapi.AppGlue, FMX.Platform.UI.Android, //:安卓UI:比如事件、窗体处理器等等 FMX.ZOrder.Android, //:安卓下:对象CSS层叠式样式的三维坐标排列管理单元 {$ENDIF ANDROID} FMX.ZOrder, //:FMX自身的对象CSS层叠式样式的三维坐标排列管理单元 1.2、窗体(含应用程序主窗体)对应关系 接口函数和方法: FMX.Platform.Android.WindowHandleToPlatform(const AHandle: TWindowHandle): TAndroidWindowHandle; 其中:const AHandle: TWindowHandle为FMX引入的窗体ANativeActivity,含应用程序的主窗体MainActivity: function WindowHandleToPlatform(const AHandle: TWindowHandle): TAndroidWindowHandle; begin //:TAndroidWindowHandle:原生的东西:访问安卓新的方式 Result := TAndroidWindowHandle(AHandle); end; function MainActivity: JFMXNativeActivity; begin //:JFMXNativeActivity:Java的东西:我们过去老的方式都是通过MainActivity在访问安卓 if TAndroidApplicationGlue.Current nil then Result := TJFMXNativeActivity.Wrap(TAndroidApplicationGlue.Current.NativeActivity.clazz) else Result := nil; end; procedure RegisterCorePlatformServices; begin PlatformAndroid := TPlatformAndroid.Create; end; procedure UnregisterCorePlatformServices; begin FreeAndNil(PlatformAndroid); end; 对应关系: Android的窗体ANativeActivity, FMX的TFORM Android的应用程序的主窗体MainActivity, FMX的MainForm:TFORM , 也代表 FMX的TApplication = class(TComponent) 1.3、Android获取FMX的窗体句柄返回Android的窗体句柄TAndroidWindowHandle 接口函数和方法: FMX.Platform.Android.WindowHandleToPlatform(const AHandle: TWindowHandle): TAndroidWindowHandle; Android的窗体句柄TAndroidWindowHandle的作用: 与FMX窗体句柄的作用类似,一旦产生或捕获了窗体句柄,就可以读写其中的子控件: FMX.Platform.UI.Android.TAndroidWindowHandle constructor Create(const AForm: TCommonCustomForm); FMX.Platform.UI.Android.TAndroidWindowHandle destructor Destroy; override; FMX.Platform.UI.Android.TAndroidWindowHandle. IsPopupForm: Boolean; FMX.Platform.UI.Android.TAndroidWindowHandle.Hide; FMX.Platform.UI.Android.TAndroidWindowHandle.Show; /// 将窗体的视图标记为脏,以便将来重新绘制:. FMX.Platform.UI.Android.TAndroidWindowHandle.Invalidate; /// 当WasFormRealignedFirstTime的只读属性值为True时:Android应用的窗体句柄在启动后launch已经重新调整了表单的布局。当加载being loaded应用程序时,我们并不知道本机窗体的边界rect,因为android稍后会在最近的布局过程中重新调整realign窗体的视图form's view的布局。但是如果开发者能知道OnShow或OnCreate事件中的client rect就好啦。为此,在android还没有重新调整表单视图之前,我们返回估计rect的大小。注意,我们不能精确定义表单的rect,因为它依赖于android的主题theme,以及android如何放置系统状态栏system status bar、系统软件按钮system software buttons等,因此我们只能估计rect的: FMX.Platform.UI.Android.TAndroidWindowHandle.WasFormRealignedFirstTime: Boolean read FWasFormRealignedFirstTime; /// 窗体的视图form's view的可读写边界属性: FMX.Platform.UI.Android.TAndroidWindowHandle.Bounds: TRectF read GetBounds write SetBounds; /// 当前窗体的只读属性: FMX.Platform.UI.Android.TAndroidWindowHandle.Form: TCommonCustomForm read FForm; /// 返回绘制窗体的表面surface的只读属性:如果尚未创建曲面surface,则可以为nil。java的东西: FMX.Platform.UI.Android.TAndroidWindowHandle.Holder: JSurfaceHolder read FHolder; /// 所有原生子视图child native views的视图组ViewGroup。java的东西: FMX.Platform.UI.Android.TAndroidWindowHandle.FormLayout: JViewGroup read FFormLayout; /// 窗体视图Form view。java的东西: FMX.Platform.UI.Android.TAndroidWindowHandle.View: JFormView read FView write FView; /// 返回窗体的原生ZOrderManager管理器,只读属性: FMX.Platform.UI.Android.TAndroidWindowHandle.ZOrderManager: TAndroidZOrderManager read GetZOrderManager; /// 返回窗体的原生MultiTouchManager多点触控管理器,只读属性: FMX.Platform.UI.Android.TAndroidWindowHandle.MultiTouchManager: TMultiTouchManagerAndroid read GetMultiTouchManager; /// 返回窗体的原生MotionManager手势管理器,只读属性:MotionManager of form. FMX.Platform.UI.Android.TAndroidWindowHandle.MotionManager: TAndroidMotionManager read GetMotionManager; /// 返回原生的TFormRender窗体提供者类及其管理器类:负责响应在原生应用程序的表面native Surface上绘制FMX的窗体,TFormRender = class(TJavaLocal, JRunnable),只读属性: FMX.Platform.UI.Android.TAndroidWindowHandle.Render: TFormRender read FRender; 二、FMX.Platform.UI.Android:安卓原生UI 四个向前定义类: TAndroidWindowHandle = class; //:原生窗体 TWindowServiceAndroid = class; //:原生本地窗体服务(delphi原生接口实现的) TAndroidMotionManager = class; //:原生手势管理器 TTextServiceAndroid = class; //:原生文本构建服务= class(TTextService) =FMX.Text.TTextService = class(内含输入法接口) 2.1、TFormRender原生窗体提供者:参见:FMX.Platform.UI.Android.TAndroidWindowHandle.Render: TFormRender read FRender; /// 原生的窗体提供者类:负责响应在原生应用程序的表面native Surface上绘制FMX的窗体,TFormRender = class(TJavaLocal, JRunnable): TFormRender = class(TJavaLocal, JRunnable) private [Weak] FHandle: TAndroidWindowHandle; FIsNeededUpdate: Boolean; { System message handler } procedure ApplicationEventHandler(const Sender: TObject; const AMessage: TMessage); //:内置应用程序级别的对象事件处理器 public //构造原生窗体,其中: //RaiseIfNil(AHandle, 'AHandle'); //:如果窗体句柄传入对象为空,报异常 //inherited Create; //:继承 //FHandle := AHandle; //:传入全局窗体句柄 //FIsNeededUpdate := False; //:初始化全局窗体是否需要更新的全局变量 //TMessageManager.DefaultManager.SubscribeToMessage(TApplicationEventMessage, ApplicationEventHandler); //:订阅应用程序事件消息及其事件处理方法 constructor Create(const AHandle: TAndroidWindowHandle); destructor Destroy; override; //解构原生窗体 { JRunnable } procedure run; cdecl; //:调用Render方法且无需更新窗体 /// 立即提供窗体(即画窗体)Renders form Immediately: procedure Render; /// 将未来提供窗体(即未来画窗体)的消费线程抛给事件总线Posts event to event bus for future rendering:简单的说: 2.1.1、引入了TAndroidHelper.MainHandler:原生操作系统下应用程序的主线程 2.1.2、引入了TAndroidHelper.MainHandler.post(Self); 即Androidapi.JNI.Os.JHandler.post(Self);将消费线程抛给事件总线,告知应用程序未来需要更新窗体: procedure PostRender; end; 2.2、TFormManager原生窗体管理器: /// Android的SurfaceView表面视图只支持2级ZOrde: 2.2.1、首先:我们使用普通的FMX窗体。 2.2.2、其次:我们使用弹出式窗体(表单)。因为在安卓下屏幕上只能显示一个普通窗体,当我们显示它时,我们隐藏了其他窗体,这些已显示的窗体的SurfaceView表面视图不能与其他正常窗体发生冲突。 这个管理器实现了上述功能。 /// TFormManager = class private FZOrderForms: TList; FDelayedHideForm: TList; //内部调用TWindowServiceAndroid.IsPopupForm(Form)和TAndroidWindowHandle(Form.Handle)处理窗体的可见性: procedure RefreshFormsVisibility; { procedure TFormManager.RefreshFormsVisibility; var I: Integer; Form: TCommonCustomForm; IsNormalFormShown: Boolean; begin IsNormalFormShown := False; for I := FZOrderForms.Count - 1 downto 0 do begin Form := TCommonCustomForm(FZOrderForms[I]); if TWindowServiceAndroid.IsPopupForm(Form) then Continue else if not IsNormalFormShown then IsNormalFormShown := True else if Form.IsHandleAllocated then TAndroidWindowHandle(Form.Handle).Hide; end; for I := 0 to FDelayedHideForm.Count - 1 do begin Form := TCommonCustomForm(FDelayedHideForm[I]); if Form.IsHandleAllocated then TAndroidWindowHandle(Form.Handle).Hide; end; FDelayedHideForm.Clear;end; } function IsSurfaceAttached(const AHandle: TAndroidWindowHandle): Boolean; public constructor Create; //:构造函数 destructor Destroy; override; //:需覆盖解构 procedure RemoveForm(const AForm: TCommonCustomForm); //:移除窗体 procedure ShowForm(const AForm: TCommonCustomForm); //:显示窗体 procedure HideForm(const AForm: TCommonCustomForm); //:隐藏窗体 procedure BringToFront(const AForm: TCommonCustomForm); //:窗体置ZOrder的最前 procedure SendToBack(const AForm: TCommonCustomForm); //:显示置ZOrder的最后 end; 2.3、TWindowServiceAndroid = class(TInterfacedObject, IFreeNotification, IFMXWindowService, IFMXMouseService) //:原生本地窗体服务(delphi原生接口实现的): protected function HasStatusBar(const AForm: TCommonCustomForm): Boolean; public constructor Create; //:构造函数:引入平台服务FMX.Platform.IFMXScreenService、FMX类型FMX.Types.IFMXTimerService、虚拟键盘FMX.VirtualKeyboard.IFMXVirtualKeyboardService和FMX.Platform.Android.MainActivity等产生窗体 destructor Destroy; override;//:需覆盖解构 class function IsPopupForm(const AForm: TCommonCustomForm): Boolean; //:返回是否为弹出窗体的类函数 function PixelToPoint(const APixel: TPointF): TPointF; //:返回像素到点 function PointToPixel(const APixel: TPointF): TPointF; //:返回点到像素 { FMX窗体平台服务类型IFMXWindowService } function FindForm(const AHandle: TWindowHandle): TCommonCustomForm; //:根据已产生的窗体句柄返回窗体 function CreateWindow(const AForm: TCommonCustomForm): TWindowHandle; //:根据窗体返回窗体句柄(即产生1个窗体) procedure DestroyWindow(const AForm: TCommonCustomForm); //: 释放窗体:窗体管理器移除窗体、清空鼠标点下事件、控件释放焦点、清空手势控件 procedure ReleaseWindow(const AForm: TCommonCustomForm); //: 释放窗体中的资源:实现了与DestroyWindow相同的功能 procedure ShowWindow(const AForm: TCommonCustomForm); //: 显示出某个窗体(弹窗状态为wsNormal,非弹窗控制wsMaximized且根据其BorderStyle属性值控制状态条的显示:BorderStyle=None,状态条可见性=false,否则为true) procedure HideWindow(const AForm: TCommonCustomForm); //: 隐藏窗体:窗体管理器TFormManager.HideForm(AForm);窗体状态WindowState最小化wsMinimized procedure BringToFront(const AForm: TCommonCustomForm); //:调用窗体管理器TFormManager将窗体置最前端 procedure SendToBack(const AForm: TCommonCustomForm); //: 调用窗体管理器TFormManager将窗体置最后端 procedure Activate(const AForm: TCommonCustomForm); //: 调用窗体管理器TFormManager将窗体置于活动状态(即TFormManager.ShowForm) function ShowWindowModal(const AForm: TCommonCustomForm): TModalResult; //: 安卓不支持模态窗体:返回错误提示 function CanShowModal: Boolean; //: 返回安卓是否支持模态方式的窗体:false procedure InvalidateWindowRect(const AForm: TCommonCustomForm; ARect: TRectF); //: 调用窗体句柄函数将窗体的视图标记为脏,以便将来以设定的边界重新绘制: procedure InvalidateImmediately(const AForm: TCommonCustomForm); //: 调用窗体句柄函数将窗体的视图立即原封不动重新绘制: procedure SetWindowRect(const AForm: TCommonCustomForm; ARect: TRectF); //: 调用窗体句柄函数设置窗体的边界且以负的偏移量将状态条的高度不包含进去: { 表单视图Form's view(即TAndroidWindowHandle(AForm.Handle).view)放置在内容视图(content view)中。它并没有填满所有的屏幕(因为有状态栏)。但是ARect是包含了 屏幕坐标系中的矩形的。为此,我们以状态条的高度的负数的偏移量进行补偿: ARect.Offset(0, -StatusBarHeight); TAndroidWindowHandle(AForm.Handle).Bounds := ARect; } function GetWindowRect(const AForm: TCommonCustomForm): TRectF; //: 调用窗体句柄函数获取窗体的边界(且以正的偏移量将状态条的高度包含进去) function GetClientSize(const AForm: TCommonCustomForm): TPointF; //: 获取FMX下窗体的原始尺寸TSizeF.cx(宽Width)和TSizeF.cy(高Height) procedure SetClientSize(const AForm: TCommonCustomForm; const ASize: TPointF); //: 调用窗体句柄函数以原生窗体的左上边界为基准设置窗体的尺寸TSizeF.cx(宽Width)和TSizeF.cy(高Height) procedure SetWindowCaption(const AForm: TCommonCustomForm; const ACaption: string); //: 安卓Android下设置窗体的标题无效,不执行任何代码 procedure SetCapture(const AForm: TCommonCustomForm); //: 捕获某窗体并将其复制给全局捕获窗体变量FCapturedForm以便将来响应MouseMove、MouseUp、ReleaseCapture时以此为基准 procedure SetWindowState(const AForm: TCommonCustomForm; const AState: TWindowState); //: 设置可见窗体和不可见窗体的状态:AForm.Visible或not AForm.Visible (*{ TScrollingWinControl } System.UITypes.TWindowState = (wsNormal, wsMinimized, wsMaximized);*) procedure ReleaseCapture(const AForm: TCommonCustomForm); //: 释放某窗体全局捕获窗体变量 function ClientToScreen(const AForm: TCommonCustomForm; const ALocalFormPoint: TPointF): TPointF; //: 以窗体当前的屏幕位置为基准将窗体(无论普通窗体还是弹窗)放大到与屏幕对齐(将状态条也包含进去) function ScreenToClient(const AForm: TCommonCustomForm; const AScreenPoint: TPointF): TPointF; overload; //: 将放大的窗体还原到原始大小和原始位置(ClientToScreen的逆函数) function GetWindowScale(const AForm: TCommonCustomForm): Single; //: 返回窗体的当前放大比例因子:32位实数:Float32 = Single; { 鼠标服务类型IFMXMouseService } function GetMousePos: TPointF; //: 返回鼠标在屏幕上的当前位置 { 鼠标事件类型Mouse handlers } procedure MouseDown(const AForm: TCommonCustomForm; const AButton: TMouseButton; const AShift: TShiftState; const AClientPoint: TPointF); //:鼠标及鼠标组合键(若手势有效则包含该点位的手势)按下的处理事件 procedure MouseMove(const AForm: TCommonCustomForm; const AShift: TShiftState; const AClientPoint: TPointF); //:鼠标及鼠标组合键(若手势有效则包含该点位的手势)移动的处理事件 procedure MouseUp(const AForm: TCommonCustomForm; const AButton: TMouseButton; const AShift: TShiftState; const AClientPoint: TPointF; const ADoClick: Boolean = True); //:鼠标及鼠标组合键(若手势有效则包含该点位的手势)抬起的处理事件 { 交互式手势类型Gestures 引用FMX.Types.IGestureControl接口 } function SendCMGestureMessage(AForm: TCommonCustomForm; AEventInfo: TGestureEventInfo): Boolean; //: 向窗体上的手势管理控件发送交互式TInteractiveGesture手势事件的记录信息的消息并返回true,若窗体无手势管理控件返回false { 文本类型Text的原生窗体服务:引入了FMX.Types.IControl } procedure BeginSelection; //: 开始选择文本 procedure EndSelection; //: 结束选择文本 function GetTextService: TTextServiceAndroid; //: 返回并获取Android原生文本服务(条件:如果窗体控件接口获得了焦点和支持文本输入) procedure SetFocusedControl(const AControl: IControl); //: 设置并获取窗体上的控件接口的焦点(条件:如果窗体控件接口获得了焦点) public property Scale: Single read FScale; //: 获取窗体的当前放大比例因子的只读属性:32位实数:Float32 = Single; property StatusBarHeight: Single read FStatusBarHeight; //: 读取当前窗体状态条高度的只读属性32位实数:Float32 = Single; property FormManager: TFormManager read FFormManager; //: 原生窗体服务获取原生窗体管理器的只读属性 property ScreenMousePos: TPointF read FScreenMousePos write FScreenMousePos; //: 原生窗体服务读写屏幕上的当前鼠标或手势触点的位置 end; 2.4、TAndroidMotionManager = class(TInterfacedObject, IFMXGestureRecognizersService) //:安卓原生交互手势 private const DblTapDelay = 300; //delay between the 2 taps LongTapDuration = 500; LongTapMovement = 10; //10 pixels - use scale to transform to points to use on each device protected function CreateGestureEventInfo(const AGesture: TInteractiveGesture; const AGestureEnded: Boolean = False): TGestureEventInfo; //:产生手势事件信息 public constructor Create(const AHandle: TAndroidWindowHandle); //:构造函数:通过传入窗体句柄参数获取全局窗体句柄并获取手势事件记录列表 destructor Destroy; override; //:需覆盖解构 procedure ProcessAndroidGestureEvents; (*//:处理Android安卓以下手势输入事件: 在满足Androidapi.Input.AMOTION_EVENT_ACTION_MASK = $ff的前提下 {Androidapi.Input} const AMOTION_EVENT_ACTION_DOWN = 0;//按下的手势开始,该动作包含初始开始位置:处理CreateDoubleTapTimer和CreateLongTapTimer AMOTION_EVENT_ACTION_UP = 1;//按下的手势完成了,动作包含了最后的释放位置,以及自上次向下或移动事件以来的任何中间点:处理:1、DoubleTap : PlatformAndroid.WindowService.SendCMGestureMessage(FHandle.Form, CreateGestureEventInfo(TInteractiveGesture.DoubleTap));处理:2、 DestroyLongTapTimer; AMOTION_EVENT_ACTION_MOVE = 2;//在手势过程中发生了变化的手势事件:按下并抬起来后(在AMOTION_EVENT_ACTION_DOWN和AMOTION_EVENT_ACTION_UP之间)。该运动包含最近的点,以及自上次向下或移动事件以来的任何中间点 AMOTION_EVENT_ACTION_CANCEL = 3;//中止当前手势。你将不会得到更多的点。你应该把这个当作一个up事件,但不执行任何您通常会执行的操作 AMOTION_EVENT_ACTION_POINTER_DOWN = 5;// AMOTION_EVENT_ACTION_POINTER_UP// *) procedure ProcessAndroidMouseEvents; (*//:处理以下鼠标事件: AMOTION_EVENT_ACTION_DOWN AMOTION_EVENT_ACTION_UP AMOTION_EVENT_ACTION_MOVE *) function HandleMotionEvent(const AEvent: JMotionEvent): Boolean;//:处理特殊类型的手势和鼠标事件 { 交互式手势辨别的FMX接口服务IFMXGestureRecognizersService: } procedure AddRecognizer(const AGesture: TInteractiveGesture; const AForm: TCommonCustomForm);//:处理原生窗口能接受的交互式手势的甄别TInteractiveGestures = set of TInteractiveGesture; //:system.Include(FEnabledInteractiveGestures, AGesture); procedure RemoveRecognizer(const AGesture: TInteractiveGesture; const AForm: TCommonCustomForm);//:取消原生窗口能接受的交互式手势TInteractiveGestures = set of TInteractiveGesture; //:system.Exclude(FEnabledInteractiveGestures, AGesture); end; 2.5、TAndroidTextInputManager = class(TInterfacedObject, IFMXKeyMappingService, IFMXTextService)//:原生文本输入管理器 protected function FindActiveForm: TCommonCustomForm; procedure KeyDown(var AKey: Word; var AKeyChar: System.WideChar; const AShift: TShiftState); procedure KeyUp(var AKey: Word; var AKeyChar: System.WideChar; const AShift: TShiftState; const AKeyDownHandled: Boolean); public constructor Create; destructor Destroy; override; function HandleAndroidKeyEvent(AEvent: PAInputEvent): Int32; //:处理对原生物理按键(对应的虚拟键值)的事件响应:Androidapi.Input中定义了原生键盘按键的常量定义 { 控件的输入法:Android view for IME } function GetEditText: JFMXEditText; //:返回1个文本控件 { 控件的文本服务接口:IFMXTextService } function GetTextServiceClass: TTextServiceClass; //:获取文本服务类 { 控件的虚拟键盘映射接口:IFMXKeyMappingService } /// Registers a platform key as the given virtual key. function RegisterKeyMapping(const PlatformKey, VirtualKey: Word; const KeyKind: TKeyKind): Boolean;//:平台键盘的键注册为虚拟键 /// Unegisters a platform key as the given virtual key. function UnregisterKeyMapping(const PlatformKey: Word): Boolean; /// 根据平台键盘的键返回虚拟键值:Obtains the virtual key from a given platform key. function PlatformKeyToVirtualKey(const PlatformKey: Word; var KeyKind: TKeyKind): Word; /// 根据虚拟键返回平台键盘的键值:Obtains the platform key from a given virtual key. function VirtualKeyToPlatformKey(const VirtualKey: Word): Word; end; 2.6、TFMXTextListener = class(TJavaLocal, JFMXTextListener) strict private [Weak] FTextService: TTextServiceAndroid; public constructor Create(const ATextService: TTextServiceAndroid); overload; { JFMXTextListener } procedure onTextUpdated(text: JCharSequence; caretPosition: Integer); cdecl; procedure onComposingText(beginPosition: Integer; endPosition: Integer); cdecl; //:撰写文本前设置起、止位置caretPosition的事件方法属性 procedure onEditorAction(actionCode: Integer); cdecl; end; 2.7、TTextServiceAndroid = class(TTextService) // FMX.Text.TTextService = class protected function GetText: string; override; procedure SetText(const AValue: string); override; function GetCaretPosition: TPoint; override; procedure SetCaretPosition(const AValue: TPoint); override; procedure SetMaxLength(const AValue: Integer); override; procedure SetCharCase(const AValue: TEditCharCase); override; procedure SetFilterChar(const AValue: string); override; public procedure InternalUpdate; //:内部更新含输入法 procedure InternalUpdateSelection; //:内部计算边界且选中文本 function CombinedText: string; override; //:多行文本合并为字符串 function TargetClausePosition: TPoint; override; procedure EnterControl(const AFormHandle: TWindowHandle); override; procedure ExitControl(const AFormHandle: TWindowHandle); override; procedure DrawSingleLine(const ACanvas: TCanvas; const ARect: TRectF; const AFirstVisibleChar: integer; const AFont: TFont; const AOpacity: Single; const AFlags: TFillTextFlags; const ATextAlign: TTextAlign; const AVTextAlign: TTextAlign = TTextAlign.Center; const AWordWrap: Boolean = False); overload; override; procedure DrawSingleLine(const ACanvas: TCanvas; const S: string; const ARect: TRectF; const AFont: TFont; const AOpacity: Single; const AFlags: TFillTextFlags; const ATextAlign: TTextAlign; const AVTextAlign: TTextAlign = TTextAlign.Center; const AWordWrap: Boolean = False); overload; override; function HasMarkedText: Boolean; override; function GetImeMode: TImeMode; override; //:获取当前输入法模式 procedure SetImeMode(const AValue: TImeMode); override; //:设置输入法模式 { 文本服务类选择文本:Selection } procedure BeginSelection; override; procedure EndSelection; override; procedure ProcessUpdate(const APos: Integer; AText: string); //:从APos位置开始选择文本字符串AText public constructor Create(const Owner: IControl; ASupportMultiLine: Boolean); override; destructor Destroy; override; end; 2.7、两个独立函数: function ConvertPixelToPoint(const APixel: TPointF): TPointF; //:根据当前窗体服务的放大比例因子TWindowServiceAndroid.FScale(APixel.X / FScale, APixel.Y / FScale)转化像素点为屏幕物理点,并返回该屏幕物理点 function ConvertPointToPixel(const APoint: TPointF): TPointF; //:根据当前窗体服务的放大比例因子TWindowServiceAndroid.FScale(APoint.X * FScale, APoint.Y * FScale)转化屏幕物理点为像素点,并返回该屏幕物理点 三、FMX.Platform.Android //:delphi FMX平台和Android安卓操作系统的对接的接口单元 //:其中引用了FMX原生调用Android原生对象的入口单元 :Androidapi.AppGlue, type {拍照的广播消息类: Broadcast messages: Taking images } TMessageCancelReceivingImage = class(TMessage); //:取消接收拍照的图片的索引Integer类 TMessageReceivedImagePath = class(TMessage) //:接收拍照的路径string类 public RequestCode: Integer; //:全局公开请求码 end; TFMXNativeActivityListener = class(TJavaLocal, JOnActivityListener) //:FMX原生窗体事件的监听类 public procedure onCancelReceiveImage(ARequestCode: Integer); cdecl; //:执行当请求取消接收图片发出消息时的事件线程队列 procedure onReceiveImagePath(ARequestCode: Integer; AFileName: JString); cdecl; //:执行当请求接收图片的路径发出消息时的事件线程队列 procedure onReceiveNotification(AIntent: JIntent); cdecl; //:执行当让某个外部应用JIntent接收通知消息时的事件线程队列 procedure onReceiveResult(ARequestCode, AResultCode: Integer; AResultObject: JIntent); cdecl; //:执行当让某个外部应用JIntent接收完毕通知消息并返回结果时的事件线程队列 procedure onRequestPermissionsResult(ARequestCode: Integer; APermissions: TJavaObjectArray; AGrantResults: TJavaArray); cdecl; end; { 安卓平台类:TPlatformAndroid } TMainThreadWakeup = class(TThread) //主线程唤醒 protected procedure Execute; override; procedure TerminatedSet; override; public constructor Create; //:TRunnable传入安卓内部发消息的匿名过程PlatformAndroid.InternalProcessMessages产生主线程唤醒实例:PlatformAndroid:全局变量PlatformAndroid: TPlatformAndroid; //uses FMX.Platform.Android destructor Destroy; override; //:释放全局FRunnable和信号灯FSignal procedure WakeUp; //:调用delphi原生系统监视器System.TMonitor.Pulse(FSignal)点亮全局脉冲信号灯FSignal线程; { 顺便谈谈线程的监视器System.TMonitor: 关于System.TMonitor及Pulse和Wait: 1、Wait将完全原子释放锁(不管递归计数如何),并阻塞调用线程直到另一个线程调用Pulse或PulseAll点亮全局脉冲信号灯。第一次重载等待函数Wait将假定assume存在锁定的对象locked object并且将要wait的对象是相同的,因此调用线程必须拥有锁lock。第二次等待Wait允许设定的monitor监视器以原子方式解锁独立的监视器锁对象monitor lock object,并在第一个等待对象上阻塞对线程的调用。 2、Wait等待将持续下去,即使超时也不会返回,直到可以再次获取监视器锁。Wait等待在经过较长时间后,如果锁定对象被另一个线程长时间持有held,之前的wait等待将可能返回false。Wait等待过程中,已恢复了返回的锁的递归级别。 3、为了正确释放一个等待线程,必须在传递给等待的完全相同的实例上调用Pulse以点亮全局脉冲信号灯,比如:TMonitor.Pulse(FSignal)。 4、Pulse all的工作原理与Pulse相同,只是它将释放所有当前等待的线程。Wait/Pulse/PulseAll的变量与传统条件变量相同。 关于TMonitor.Enter和TMonitor.Exit: 1、用1个超时毫秒选项Enter进入监视器锁。在超时毫秒的时间内进入监视器锁。如果过程执行完毕且返回,则可以假定认为锁已获取了。 2、用timeout选项将返回一个布尔状态,指示是否已获得锁(True)或尝试在获取锁之前超(False)。 3、以无限时间INFINITE调用Enter与不带timeout选项调用Enter等效。 4、TryEnter函数仅企图获取锁并立即返回,无论锁是否被获取到。 5、带0毫秒timeout选项的Enter函数在功能上等同于TryEnter,相当于立即进入监视器锁。 6、Exit可能会释放由Enter或TryEnter函数获取的锁。因为Enter/TryEnter是1个提供者rentrant,你必须成对匹配每个Eneter和相应的Exit退出调用。仅最后一个Exit退出调用会释放锁并让其他线程获得它。 7、如果调用线程没有拥有锁lock而被Exit,运行时会报错reMonitorNotLocked。 通常的调用格式TMonitor.Enter和TMonitor.Exit: TMonitor.Enter(AObject,0); AObject.Lock; //...你的代码... AObject.UnLock; TMonitor.Exit(AObject); } end; TRecognizerServiceMap = class(TInterfacedObject, IFMXGestureRecognizersService) //:映射窗体的交互式手势服务 procedure AddRecognizer(const ARec: TInteractiveGesture; const AForm: TCommonCustomForm); procedure RemoveRecognizer(const ARec: TInteractiveGesture; const AForm: TCommonCustomForm); end; TRunnable = class; //:向前申明类,详见后续描述:是Android上一个java应用程序在运行时的JNI的桥接类,为FMX.Platform.Android访问它提供入口:Androidapi.JNIBridge.TJavaLocal = class abstract (TInterfacedObject, ILocalObject, IJava) { TRunnable = class(TJavaLocal, JRunnable) private FCallback: TProc; public constructor Create(const ACallback: TProc); //TRttiContext运行时类型定义并产生回调接口,继承Androidapi.JNIBridge.Create { JRunnable } procedure run; cdecl; //:执行ACallback回调 end; } TPlatformAndroid = class(TInterfacedObject, IFMXApplicationEventService, IFMXApplicationService)//:Android平台接口类、应用程序服务接口类、应用程序消息处理接口类(怎样处理接受到的消息的类),内部私有绑定了Android原生事件处理BindAppGlueEvents,处理以下3种消息: protected function HandleAndroidInputEvent(const AAppGlue: TAndroidApplicationGlue; const AEvent: PAInputEvent): Int32;//:1、处理键盘输入消息 procedure HandleApplicationCommandEvent(const AAppGlue: TAndroidApplicationGlue; const ACommand: TAndroidApplicationCommand);//:2、处理Android原生各种命令消息TAndroidApplicationCommand procedure HandleContentRectChanged(const AAppGlue: TAndroidApplicationGlue; const ARect: TRect);//: 3、处理Android原生屏幕服务更新屏幕区域显示内容的消息FMX.Platform.Screen.Android.TAndroidScreenServices.UpdateDisplayInformation public constructor Create; //:构造函数顺序执行了:1、绑定了Android原生应用程序消息事件处理过程BindAppGlueEvents;2、产生这些核心服务(TAndroidLoggerService、TAndroidTimerService、TAndroidSaveStateService、TAndroidScreenServices、TAndroidMetricsServices、TAndroidDeviceServices、TVirtualKeyboardAndroid、TAndroidGraphicsServices、TWindowServiceAndroid、TRecognizerServiceMap、TAndroidTextInputManager、TMessageQueueIdleHandler);3、导入delphi原生产生的应用程序TApplication.Create(nil),delphi代码开始介入Android操作系统;4、产生原生事件监听类TFMXNativeActivityListener;5、产生原生主线程唤醒类TMainThreadWakeup;6、加入可支持VR眼镜应用的标记;7、注册RegisterServices以上服务;8、注册唤醒主线程RegisterWakeMainThread;9、注册应用空闲句柄RegisterIdleHandler destructor Destroy; override; //:解构Create中的项目并继承 procedure InternalProcessMessages; //:发出Android平台内部处理消息的指令 function ProcessOnIdleEvent: Boolean; //:返回并处理Android平台空闲等待事件(如果应用程序没有终止运行,则处理Action的空闲事件发消息、处理内部计时器,并处理异常) { 以下调用FMX应用程序服务类接口:IFMXApplicationService } procedure Run; //:1、调用原生应用程序仿真例程Androidapi.AppGlue.app_dummy,确保"Androidapi.AppGlue.pas"是保存在引用列表中,以便导出ANativeActivity.Create的回调(即导出ANativeActivity_onCreate),详见:《我对学习delphiTeacher的的学习和研究点滴体会之一》https://blog.csdn.net/pulledup/article/details/105040901 : 章节三、关于Androidapi.AppGlue.pas 2、调用内部处理消息InternalProcessMessages ;3、赋值全局变量FRunning:设置应用程序处于运行状态=true function HandleMessage: Boolean; //:函数:即调用内部处理消息InternalProcessMessages,且返回=false procedure WaitMessage; //:过程:即调用内部处理消息InternalProcessMessages function GetDefaultTitle: string; //:返回java应用程序的默认名称 function GetTitle: string; //:返回SetTitle所赋值的全局变量FTitle对应的原生应用程序的名称 procedure SetTitle(const AValue: string); //:赋值的全局变量FTitle:原生应用程序的名称 function GetVersionString: string; //:通过JNI运行时上下文环境可视化视图的包管理器获取的包信息来返回应用程序的版本字符串;Androidapi.Helpers.JContext(Androidapi.JNI.GraphicsContentViewText.JContext)->AppContext.getPackageManager.getPackageInfo(Androidapi.JNI.GraphicsContentViewText.getPackageManager)//:可用于判断后执行动态升级App的例程 function Running: Boolean; //:获取全局变量FRunning:判断应用程序是否处于运行状态 function Terminating: Boolean; //:获取全局变量FTerminating:判断应用程序是否处于终止状态 procedure Terminate; //:赋值全局变量FRunning和FTerminating:设置应用程序处于非运行和终止状态并发消息,内部处理HandleApplicationCommandEvent原生Android应用程序命令事件消息并保存应用程序的实例状态;发消息给主线程让delphi退出对JNI的java的调用:Androidapi.NativeActivity.ANativeActivity_finish(System.DelphiActivity); //:可从任何线程调用该方法。{ 当我们手动结束应用Activity时(即Application.Terminate时),Android不会触发OnSaveInstanceState保存实例状态的命令事件,因为它仅在Android操作系统要终止我们的应用来回收资源的情况下才会触发。在这种情况下, 要正确终止已启动的应用程序,我们必须手动调用OnSaveInstanceState以确保 在应用程序Application关闭close之前调用了delphi的TForm.OnSaveState,方法就是内部受保护调用:HandleApplicationCommandEvent(TAndroidApplicationGlue.Current, TAndroidApplicationCommand.SaveState); 即delphi应用程序彻底退出Android操作系统的方法是:APlatformAndroid:=TPlatformAndroid.Create; APlatformAndroid.Terminate; 且在主线程MainForm.OnClose中: APlatformAndroid. Destroy; } { IFMXApplicationEventService } procedure SetApplicationEventHandler(AEventHandler: TApplicationEventHandler); function HandleApplicationEvent(const AEvent: TApplicationEvent): Boolean; public property DeviceManager: TAndroidDeviceServices read FDeviceServices; property Logger: TAndroidLoggerService read FLoggerService; property Metrics: TAndroidMetricsServices read FMetricsServices; property SaveStateManager: TAndroidSaveStateService read FSaveStateService; property ScreenManager: TAndroidScreenServices read FScreenServices; property TextInputManager: TAndroidTextInputManager read FTextInputManager; property TimerManager: TAndroidTimerService read FTimerService; property VirtualKeyboard: TVirtualKeyboardAndroid read FVirtualKeyboardService; property WindowService: TWindowServiceAndroid read FWindowService; end; TRunnable = class(TJavaLocal, JRunnable) private FCallback: TProc; public constructor Create(const ACallback: TProc); { JRunnable } procedure run; cdecl; end; var PlatformAndroid: TPlatformAndroid; function WindowHandleToPlatform(const AHandle: TWindowHandle): TAndroidWindowHandle; function MainActivity: JFMXNativeActivity; procedure RegisterCorePlatformServices; procedure UnregisterCorePlatformServices;
|
CopyRight 2018-2019 实验室设备网 版权所有 |